package replay

import (
	"bytes"
	"context"
	"encoding/binary"
	"io"
	"os"
	"runtime"
	"sync"
	"time"
)

// Player 用于回放，支持协议前缀和非法数据跳过
type Player struct {
	path           string
	f              *os.File
	speed          float64
	mu             sync.RWMutex
	protocolPrefix []byte // 协议前缀
}

var (
	MIN_TIME              = time.Date(1981, 1, 1, 0, 0, 0, 0, time.UTC)
	MAX_TIME              = time.Date(2081, 12, 31, 23, 59, 59, 0, time.UTC)
	MAX_BLOCK_SIZE uint32 = 1024 * 1024 * 5
)

var Warnf = func(format string, v ...interface{}) {

}

// NewPlayer 打开文件准备回放.
func NewPlayer(path string) (*Player, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	return &Player{f: f, path: path, speed: 1}, nil
}

// SetProtocolPrefix 设置协议前缀
func (p *Player) SetProtocolPrefix(prefix []byte) {
	p.mu.Lock()
	defer p.mu.Unlock()
	p.protocolPrefix = make([]byte, len(prefix))
	copy(p.protocolPrefix, prefix)
}

// SetSpeed 动态调整回放倍数.
func (p *Player) SetSpeed(s float64) {
	p.mu.Lock()
	p.speed = s
	p.mu.Unlock()
}

// findProtocolPrefix 在当前文件位置开始查找协议前缀
func (p *Player) findProtocolPrefix() error {
	prefixLen := len(p.protocolPrefix)
	if prefixLen == 0 {
		return nil
	}

	// 创建一个足够大的缓冲区来读取数据
	buf := make([]byte, prefixLen)

	// 从当前位置开始，逐步查找协议前缀
	for {
		// 读取prefixLen个字节
		_, err := io.ReadFull(p.f, buf)
		if err != nil {
			return err
		}

		// 检查是否匹配协议前缀
		if bytes.Equal(buf, p.protocolPrefix) {
			// 匹配成功，返回
			return nil
		}

		// 不匹配，将第一个字节移出，为下一次读取做准备
		// 将文件指针回退prefixLen-1个字节，这样下一次读取会从第二个字节开始
		currentPos, _ := p.f.Seek(0, 1)
		p.f.Seek(currentPos-int64(prefixLen-1), 0)
	}
}

// readOne 读取一条记录；返回 (时间戳, 数据, 错误).
func (p *Player) readOne() (ts time.Time, data []byte, err error) {
	for {
		var nano int64
		var ln uint32

		// 如果设置了协议前缀，则先查找并验证
		if len(p.protocolPrefix) > 0 {
			// 查找协议前缀
			err = p.findProtocolPrefix()
			if err != nil {
				return time.Time{}, nil, err
			}
		}

		// 读取时间戳
		if err = binary.Read(p.f, binary.LittleEndian, &nano); err != nil {
			return time.Time{}, nil, err
		}

		// 验证时间戳是否合理（在合理范围内，比如1981年到2081年之间）
		t := time.Unix(0, nano)
		if t.Before(MIN_TIME) || t.After(MAX_TIME) {
			// 时间戳不合理，说明可能不是有效记录
			if len(p.protocolPrefix) > 0 {
				// 寻找下一个协议前缀
				p.f.Seek(-8, io.SeekCurrent) // 回退8字节（时间戳的大小）
				err = p.findProtocolPrefix()
				if err != nil {
					return time.Time{}, nil, err
				}
				continue
			} else {
				// 寻找下一个值
				p.f.Seek(-8, io.SeekCurrent) // 回退8字节（时间戳的大小）
				findErr := p.findNextValidRecord()
				if findErr != nil {
					return time.Time{}, nil, findErr
				}
				// 读取到值
				p.f.Seek(-12, io.SeekCurrent) // 回退8字节 + ln（时间戳的大小）
				continue
			}
		}

		// 读取数据长度
		if err = binary.Read(p.f, binary.LittleEndian, &ln); err != nil {
			return time.Time{}, nil, err
		}

		// 检查长度是否合理（防止恶意数据导致内存溢出）
		if ln > MAX_BLOCK_SIZE { // 限制单条数据最大5MB
			// 跳过这个记录，继续读取下一个
			_, err := p.f.Seek(int64(ln), 1) // 跳过无效数据
			if err != nil {
				return time.Time{}, nil, err
			}
			continue
		}

		ts = time.Unix(0, nano)
		data = make([]byte, ln)
		_, err = io.ReadFull(p.f, data)
		if err != nil {
			return time.Time{}, nil, err
		}

		return ts, data, nil
	}
}

// findNextValidRecord 查找下一个有效的记录
func (p *Player) findNextValidRecord() (err error) {
	// 尝试寻找下一个可能的有效记录
	buf := make([]byte, 1)

	// 记录开始位置，用于检测是否已经搜索了整个文件
	startPos, err := p.f.Seek(0, io.SeekCurrent)
	if err != nil {
		return err
	}

	for {
		var nano int64
		var ln uint32

		// 读取时间戳
		if err = binary.Read(p.f, binary.LittleEndian, &nano); err != nil {
			return err
		}

		// 验证时间戳是否合理（在合理范围内，比如1981年到2081年之间）
		t := time.Unix(0, nano)
		if t.Before(MIN_TIME) || t.After(MAX_TIME) {
			// 时间戳不合理，跳过这个字节并继续寻找
			// 回退7个字节（因为已经读了8字节的时间戳，要回到下一个字节开始的位置）
			p.f.Seek(-7, io.SeekCurrent)

			// 检查是否已经搜索了一圈回到起始位置
			newPos, _ := p.f.Seek(0, io.SeekCurrent)
			if newPos <= startPos {
				// 检查是否文件已经到达末尾
				testPos, _ := p.f.Seek(0, io.SeekCurrent)
				_, err = p.f.Read(buf)
				if err == io.EOF {
					// 回到原来位置再尝试读取一次，防止边界情况
					p.f.Seek(testPos, io.SeekStart)
					return io.EOF
				}
				// 如果不是EOF，回退
				p.f.Seek(newPos, io.SeekStart)
			}
			continue
		}

		// 读取数据长度
		if err = binary.Read(p.f, binary.LittleEndian, &ln); err != nil {
			return err
		}

		// 验证长度是否合理
		if ln > MAX_BLOCK_SIZE { // 限制单条数据最大5MB
			// 长度不合理，跳过这个字节并继续寻找
			p.f.Seek(-(8 + 4 - 1), io.SeekCurrent)
			continue
		}

		return nil
	}
}

// Replay 按记录时间间隔回放，支持倍数；ctx 可随时取消.
func (p *Player) Replay(ctx context.Context, onData func(ts time.Time, data []byte)) error {
	var lastTS time.Time
	first := true
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		default:
		}

		ts, data, err := p.readOne()
		if err == io.EOF {
			return nil
		}
		if err != nil {
			// 如果是非法数据错误，尝试继续处理
			if err == io.ErrUnexpectedEOF || err == io.EOF {
				// 继续尝试读取下一个记录
				continue
			}
			return err
		}

		p.mu.RLock()
		speed := p.speed
		p.mu.RUnlock()

		if !first && speed > 0 {
			interval := ts.Sub(lastTS)
			wait := time.Duration(float64(interval) / speed)

			if wait <= 0 {
				runtime.Gosched()
			} else {
				if wait > time.Second*5 {
					Warnf("wait too long %v, pre %s, next %s", wait.Truncate(time.Second), lastTS.Format(time.RFC3339), ts.Format(time.RFC3339))
				}
				t := time.NewTimer(wait)
				select {
				case <-t.C:
				case <-ctx.Done():
					t.Stop()
					return ctx.Err()
				}
			}
		}
		first = false
		lastTS = ts

		onData(ts, data) // ← 把时刻传出去
	}
}

// Close 关闭文件.
func (p *Player) Close() error { return p.f.Close() }
